#pragma region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU Lesser General Public License.
/// </copyright>
#pragma endregion

#include "stdafx.h"

OSHTransform::OSHTransform()
{
	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose,
		"OSHTransform::OSHTransform() : 0x%08X\r\n", this);

	m_ulMaxImageBands = 0;
    m_dwOptionFlags = DXBOF_SAME_SIZE_INPUTS; //DXBOF_OUTPUT_MESHBUILDER;
    //m_dwMiscFlags = /*DXTMF_QUALITY_SUPPORTED |*/ DXTMF_BOUNDS_SUPPORTED | DXTMF_PLACEMENT_SUPPORTED;
    m_ulMaxInputs = 1;
    m_ulNumInRequired = 1;
}

OSHTransform::~OSHTransform()
{
	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose,
		"OSHTransform::~OSHTransform() : 0x%08X\r\n", this);
}

STDMETHODIMP OSHTransform::get_OSHSource(VARIANT* pVal)
{
	if(pVal == NULL) return E_POINTER;

	CComVariant result;
	result.vt = VT_NULL;
	
	if(_oshSource != NULL)
		result = _oshSource;

	HRESULT hr = result.Detach(pVal);

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose,
		"OSHTransform::get_OSHSource() : 0x%08X\r\n", hr);

	return hr;
}

STDMETHODIMP OSHTransform::put_OSHSource(VARIANT newVal)
{
	HRESULT hr = S_OK;
	if(newVal.vt == VT_NULL)
	{
		_oshSource = NULL;
	}
	else if(newVal.vt == VT_DISPATCH)
	{
		_oshSource = NULL;
		if(newVal.pdispVal != NULL)
			hr = newVal.pdispVal->QueryInterface(&_oshSource);
	}
	else
		hr = E_INVALIDARG;

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose,
		"OSHTransform::put_OSHSource(0x%08X) : 0x%08X\r\n", newVal.pdispVal, hr);

	return hr;
}

STDMETHODIMP OSHTransform::SetMiscFlags(DWORD dwMiscFlags)
{
	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose,
		"OSHTransform::SetMiscFlags(0x%08X)\r\n", dwMiscFlags);

	if(dwMiscFlags == 3) return S_OK;
	return CDXBaseNTo1::SetMiscFlags(dwMiscFlags);
}

STDMETHODIMP OSHTransform::Execute(const GUID* pRequestID, const DXBNDS* pOutBounds, const DXVEC* pPlacement)
{
	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::Execute()\r\n");
	return CDXBaseNTo1::Execute(pRequestID, pOutBounds, pPlacement);
}

HRESULT OSHTransform::OnSetup(DWORD)
{
	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose,
		"OSHTransform::OnSetup()\r\n");

	return S_OK;
} 


#define ATLTRACE_DXBNDS(category, b) \
{ \
	if(b == NULL) \
		ATLTRACE(category, TraceLevel::Verbose, "DXBNDS(NULL)"); \
	else if(b->eType == DXBT_DISCRETE) \
		ATLTRACE(category, TraceLevel::Verbose, \
			"DXBNDS(DXBT_DISCRETE, DXDBND([%d-%d], [%d-%d], [%d-%d], [%d-%d]))", \
			b->u.D[0].Min, b->u.D[0].Max, \
			b->u.D[1].Min, b->u.D[1].Max, \
			b->u.D[2].Min, b->u.D[2].Max, \
			b->u.D[3].Min, b->u.D[3].Max); \
	else if(b->eType == DXBT_CONTINUOUS) \
		ATLTRACE(category, TraceLevel::Verbose, \
			"DXBNDS(DXBT_CONTINUOUS, DXCBND([%f-%f], [%f-%f], [%f-%f], [%f-%f]))", \
			b->u.C[0].Min, b->u.C[0].Max, \
			b->u.C[1].Min, b->u.C[1].Max, \
			b->u.C[2].Min, b->u.C[2].Max, \
			b->u.C[3].Min, b->u.C[3].Max); \
}

#define ATLTRACE_DXVEC(category, v) \
{ \
	if(v == NULL) \
		ATLTRACE(category, TraceLevel::Verbose, "DXVEC(NULL)"); \
	else if(v->eType == DXBT_DISCRETE) \
		ATLTRACE(category, TraceLevel::Verbose, \
			"DXVEC(DXBT_DISCRETE, DXDBND(%d, %d, %d, %d))", \
			v->u.D[0], v->u.D[1], v->u.D[2], v->u.D[3]); \
	else if(v->eType == DXBT_CONTINUOUS) \
		ATLTRACE(category, TraceLevel::Verbose, \
			"DXVEC(DXBT_CONTINUOUS, DXCBND(%f, %f, %f, %f))", \
			v->u.C[0], v->u.C[1], v->u.C[2], v->u.C[3]); \
}

HRESULT OSHTransform::OnExecute(const GUID* pRequestID, const DXBNDS* pClipBnds, const DXVEC* pPlacement)
{
	HRESULT hr = S_OK;

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute(Clip  :");
	ATLTRACE_DXBNDS(atlTraceTransform, pClipBnds);
	ATLTRACE(atlTraceTransform, TraceLevel::Verbose, ")\r\n");

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute(Placem: ");
	ATLTRACE_DXVEC(atlTraceTransform, pPlacement);
	ATLTRACE(atlTraceTransform, TraceLevel::Verbose, ")\r\n");

	CDXDBnds inBounds(InputSurface(0), hr);
	if(FAILED(hr)) return hr;

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute(Input :");
	ATLTRACE_DXBNDS(atlTraceTransform, static_cast<DXBNDS*>(&inBounds));
	ATLTRACE(atlTraceTransform, TraceLevel::Verbose, ")\r\n");

	CDXDBnds outBounds(OutputSurface(), hr);
	if(FAILED(hr)) return hr;

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute(Output:");
	ATLTRACE_DXBNDS(atlTraceTransform, static_cast<DXBNDS*>(&outBounds));
	ATLTRACE(atlTraceTransform, TraceLevel::Verbose, ")\r\n");

	if(pClipBnds != NULL)
	{
		inBounds.IntersectBounds(inBounds, *pClipBnds);
	}

	CDXDBnds outBoundsCorrected = inBounds;
	if(pPlacement != NULL)
	{
		outBoundsCorrected.SetPlacement(*pPlacement);
	}

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute(Src   :");
	ATLTRACE_DXBNDS(atlTraceTransform, static_cast<DXBNDS*>(&inBounds));
	ATLTRACE(atlTraceTransform, TraceLevel::Verbose, ")\r\n");

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute(Dest  :");
	ATLTRACE_DXBNDS(atlTraceTransform, static_cast<DXBNDS*>(&outBoundsCorrected));
	ATLTRACE(atlTraceTransform, TraceLevel::Verbose, ")\r\n");

	bool inputIsDirty = IsInputDirty(0);
	if(inputIsDirty)
		ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute() : Input is dirty\r\n");

	//ATLASSERT(FALSE);

	DXSAMPLE* samples = NULL;
	do
	{
		int height = inBounds.Height();
		int width = inBounds.Width();

		hr = DXBitBlt(OutputSurface(), outBoundsCorrected, InputSurface(0), inBounds,
			m_dwBltFlags | DXBOF_DITHER | DXBOF_DO_OVER, m_ulLockTimeOut);
		if(FAILED(hr)) break;

		ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute() : DXBitBlt done.\r\n");

		// Create OSH bitmap
		if(_oshSource != NULL && inputIsDirty)
		{
			VARIANT_BOOL visible;
			hr = _oshSource->get_Visible(&visible);
			if(FAILED(hr)) break;

			if(visible == VARIANT_TRUE)
			{
				ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute() : UnpackBitmap.\r\n");

				hr = UnpackBitmap(&samples);
				if(FAILED(hr)) break;

				ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute() : SaveBitmap.\r\n");

				hr = SaveBitmap(samples, width, height);
			}
		}

		//BITMAPINFO info = {0};
		//info.bmiHeader.biSize = sizeof(info.bmiHeader);
		//info.bmiHeader.biWidth = width;
		//info.bmiHeader.biHeight = height;
		//info.bmiHeader.biPlanes = 1;
		//info.bmiHeader.biBitCount = 32;
		//info.bmiHeader.biCompression = 0;
		//info.bmiHeader.biSizeImage = height * width * sizeof(DXSAMPLE);
		//info.bmiHeader.biXPelsPerMeter = 0;
		//info.bmiHeader.biYPelsPerMeter = 0;
		//info.bmiHeader.biClrUsed = 0;
		//info.bmiHeader.biClrImportant = 0;
		//static int count = 0;
		//wchar_t path[MAX_PATH];
		//wsprintf(path, L"C:\\test-%d%s-%d.bmp", count++, IsInputDirty(0) ? L"-dirty" : L"", inNativeType);
		//hr = Utils::SaveBitmapFile(path, &info, (BYTE*)samples);

		//CComPtr<IDXARGBReadPtr> pInA;
		//hr = InputSurface(0)->LockSurface(NULL, m_ulLockTimeOut, DXLOCKF_READ,
		//	IID_IDXARGBReadPtr, (void**)&pInA, NULL);
		//if(FAILED(hr)) return hr;

		//CComPtr<IDXARGBReadWritePtr> pOut;
		//hr = OutputSurface()->LockSurface(NULL, m_ulLockTimeOut, DXLOCKF_READWRITE,
		//	IID_IDXARGBReadWritePtr, (void**)&pOut, NULL);
		//if(FAILED(hr)) return hr;

		//pOut->CopyRect(NULL, &inBounds, pInA, NULL, FALSE);
		ClearDirty();

	}
	while(!this);

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute() : 0x%08X\r\n", hr);

	//if(samples != NULL)
	//	delete[] samples;

	return S_OK;
}

HRESULT OSHTransform::UnpackBitmap(DXSAMPLE** pSamples)
{
	if(pSamples == NULL) return E_POINTER;
	*pSamples = NULL;

	HRESULT hr = S_OK;
	CDXDBnds inBounds(InputSurface(0), hr);
	if(FAILED(hr)) return hr;

	int height = inBounds.Height();
	int width = inBounds.Width();

	static DXSAMPLE* samples = new DXSAMPLE[height * width];
	//if(samples == NULL)
	//	return E_OUTOFMEMORY;

	CComPtr<IDXARGBReadPtr> inPtr;
	hr = InputSurface(0)->LockSurface(&inBounds, INFINITE, DXLOCKF_READ,
		IID_IDXARGBReadPtr, (void**)&inPtr, NULL);
	if(FAILED(hr))
	{
		delete[] samples;
		return hr;
	}

	ATLTRACE_TS(atlTraceTransform, TraceLevel::Verbose, "OSHTransform::OnExecute() : UnpackRect.\r\n");

	//DXSAMPLEFORMATENUM inNativeType = inPtr->GetNativeType(NULL);

	DXPACKEDRECTDESC desc = {0};
	desc.bPremult = FALSE;
	desc.lRowPadding = 0;
	desc.pSamples = samples;
	desc.rect.left = 0;
	desc.rect.top = 0;
	desc.rect.right = inBounds.Width();
	desc.rect.bottom = inBounds.Height();
	inPtr->UnpackRect(&desc);

	*pSamples = samples;
	return hr;
}

HRESULT OSHTransform::SaveBitmap(DXSAMPLE* samples, int width, int height)
{
	if(_oshSource == NULL) return E_UNEXPECTED;
	if(samples == NULL) return E_INVALIDARG;
	if(width <= 0 || height <= 0) return E_INVALIDARG;

	CComPtr<OSHLibrary::IBitmap> oshBitmap;
	HRESULT hr = _oshSource->CreateBitmap(width, height, &oshBitmap);
	if(FAILED(hr)) return hr;

	LONG_PTR bitmapInfoPtr;
	hr = oshBitmap->get_BitmapInfo(&bitmapInfoPtr);
	if(FAILED(hr)) return hr;

	BITMAPINFOHEADER* bitmapInfoHeader = reinterpret_cast<BITMAPINFOHEADER*>(bitmapInfoPtr);
	BYTE* bitmapData = reinterpret_cast<BYTE*>(bitmapInfoHeader) + bitmapInfoHeader->biSize;
	DWORD* bitmapSamples = reinterpret_cast<DWORD*>(bitmapData);

	// Copy samples. Origin of coordinates of a bitmap is at the bottom-left corner.
	DXSAMPLE* srcPrt = samples;
	DWORD transparentColor = 0x00C0C0C0;
	for(int y = 0; y < height; y++)
	{
		DWORD* destPrt = bitmapSamples + ((height - y - 1) * width);
		for(int x = 0; x < width; x++)
		{
			DWORD sample = *(srcPrt++);
			if(sample == 0x0002050A || ((sample & 0xFF000000) == 0))
				sample = transparentColor;
			//if(sample == transparentColor)
			//	sample &= 0x00FFFFFF;	// completely transparent
			//else
			//	sample |= 0xFF000000;	// completely opaque
			*(destPrt++) = sample;
		}
	}

	hr = _oshSource->ShowAndCloseBitmap(oshBitmap);
	if(FAILED(hr)) return hr;

	return hr;
}

